package cn.chenfeifx.core.controls.skins;

import cn.chenfeifx.core.common.FxUtil;
import cn.chenfeifx.core.controls.CFTab;
import cn.chenfeifx.core.controls.CFTabPane;
import javafx.animation.*;
import javafx.beans.binding.Bindings;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.geometry.Bounds;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.SkinBase;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Region;
import javafx.scene.shape.Rectangle;
import javafx.util.Callback;
import javafx.util.Duration;
import org.kordamp.ikonli.antdesignicons.AntDesignIconsOutlined;
import org.kordamp.ikonli.javafx.FontIcon;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.Callable;

public class CFTabPaneSkin extends SkinBase<CFTabPane> {

    private final CFTabPane cfTabPane;
    private final BorderPane borderPane;
    //
    private final Header header;
    private final ToggleGroup toggleGroup = new ToggleGroup();
    private final DoubleProperty barHeight = new SimpleDoubleProperty(2);

    public CFTabPaneSkin(CFTabPane cfTabPane) {
        super(cfTabPane);
        this.cfTabPane = cfTabPane;
        this.borderPane = new BorderPane();
        header = new Header();
        borderPane.setTop(header);
        borderPane.getStyleClass().add("cf-tab-pane");
        //
        getChildren().setAll(borderPane);
    }

    @Override
    protected void layoutChildren(double contentX, double contentY, double contentWidth, double contentHeight) {
        super.layoutChildren(contentX, contentY, contentWidth, contentHeight);
    }

    /**
     * 头
     */
    public class Header extends Region {
        private final Pane container = new Pane();
        private final Pane tabBox = new HBox();
        private final Rectangle bar = new Rectangle();
        private final Rectangle activeBar = new Rectangle();
        //
        private final Button nextButton = new Button();
        private final Button prevButton = new Button();

        public Header() {
            FxUtil.clipRect(this); // 裁剪,使内部元素不超出当前范围
            getStyleClass().add("header");
            getChildren().addAll(container, nextButton, prevButton);
            container.heightProperty().addListener((observable, oldValue, newValue) -> setHeight(newValue.doubleValue()));
            container.getStyleClass().add("container");
            container.getChildren().setAll(tabBox, bar, activeBar);
            tabBox.setMaxSize(USE_PREF_SIZE, USE_PREF_SIZE);
            tabBox.getStyleClass().add("tab-box");
            //
            bar.heightProperty().bind(barHeight);
            bar.widthProperty().bind(Bindings
                    .createObjectBinding(
                            (Callable<Number>) () -> Math.max(getWidth(), container.getWidth()),
                            widthProperty(), container.widthProperty()));
            bar.getStyleClass().add("bar");
            bar.yProperty().bind(tabBox.heightProperty());
            //
            activeBar.setWidth(0);
            activeBar.heightProperty().bind(barHeight);
            activeBar.getStyleClass().add("active-bar");
            activeBar.yProperty().bind(tabBox.heightProperty());
            //
            nextButton.getStyleClass().add("roll-button");
            nextButton.setGraphic(FontIcon.of(AntDesignIconsOutlined.RIGHT));
            nextButton.layoutYProperty().bind(heightProperty().subtract(nextButton.heightProperty()).divide(2));
            nextButton.layoutXProperty().bind(widthProperty().subtract(nextButton.widthProperty()).subtract(10));
            nextButton.managedProperty().bind(Bindings
                    .createObjectBinding(() -> tabBox.getWidth() + container.getLayoutX() > getWidth(),
                            container.layoutXProperty(), tabBox.widthProperty(), widthProperty()));
            nextButton.visibleProperty().bind(nextButton.managedProperty());
            //
            prevButton.getStyleClass().add("roll-button");
            prevButton.setGraphic(FontIcon.of(AntDesignIconsOutlined.LEFT));
            prevButton.layoutYProperty().bind(nextButton.layoutYProperty());
            prevButton.setLayoutX(10);
            prevButton.managedProperty().bind(container.layoutXProperty().isNotEqualTo(0));
            prevButton.visibleProperty().bind(prevButton.managedProperty());
            //
            initialize();
            registerChangeListener(widthProperty(), observableValue -> {
                // 当宽度大于实际TabBox的宽度并且container容器LayoutX为负值时,container容器向右移
                if (container.getLayoutX() < 0 && tabBox.getWidth() + container.getLayoutX() < getWidth()) {
                    double v = getWidth() - (tabBox.getWidth() + container.getLayoutX());
                    container.setLayoutX(container.getLayoutX() + v);
                } else if (container.getLayoutX() > 0) { // container容器LayoutX的值不可能大于0的,这里要处理下.
                    container.setLayoutX(0);
                }
            });
        }

        private void initialize() {
            // 是否显示activeLine
            activeBar.opacityProperty().bind(Bindings
                    .createObjectBinding(
                            (Callable<Number>) () -> toggleGroup.getSelectedToggle() == null ? 0 : 1,
                            toggleGroup.selectedToggleProperty()));
            // 监听tabs添加和删除
            cfTabPane.getTabs().addListener((ListChangeListener<CFTab>) c -> {
                ObservableList<Node> boxChildren = tabBox.getChildren();
                List<CFTab> removeList = new ArrayList<>();
                while (c.next()) {
                    if (c.wasAdded()) { // 添加
                        addTabs(c.getAddedSubList());
                    }
                    if (c.wasRemoved()) { // 删除
                        List<? extends CFTab> removedList = c.getRemoved();
                        removeList.addAll(removedList);
                    }
                }
                boolean selected = false; // 删除的数据是否包含选中的

                if (!removeList.isEmpty()) {
                    for (CFTab tab : removeList) {
                        selected = tab.isSelected();
                        // 避免键盘事件起作用,下边任意一行代码都能解决问题.
                        // this.setDisable(true);
                        tab.setToggleGroup(null);
                    }
                    CFTab selectedTab = null;
                    if (selected && removeList.size() == 1 && boxChildren.size() > 1) { // 删除的数量为1时
                        int indexOf = boxChildren.indexOf(removeList.get(0).getTabButton().getParent());
                        if (indexOf < boxChildren.size() - 1) {
                            selectedTab = (CFTab) boxChildren.get(indexOf + 1).getUserData();
                        } else {
                            selectedTab = (CFTab) boxChildren.get(indexOf - 1).getUserData();
                        }
                    }
                    boxChildren.removeAll(removeList.stream().map(CFTab::getTabNode).toList());
                    if (!selected) { // 不包含选中的
                        tabBox.layout();
                        activeBarMove();
                    }
                    if (selected && selectedTab != null) {
                        tabBox.layout();
                        selectedTab.setSelected(true);
                    }
                }
            });
            // 选中事件
            toggleGroup.selectedToggleProperty().addListener((observable, oldValue, newValue) -> {
                if (newValue != null) {
                    CFTab cfTab = (CFTab) ((ToggleButton) newValue).getParent().getUserData();
                    // 动画
                    activeBarMove(); // activeBar动画
                    containerMove(null);
                    // 回调
                    Callback<CFTab, Node> tabResult = cfTabPane.getTabResult();
                    if (tabResult != null) {
                        Node call = tabResult.call(cfTab);
                        if (call != null) { // 设置内容
                            borderPane.setCenter(call);
                        }
                    }
                }
            });
            // 翻页动画
            prevButton.setOnMouseClicked(event -> {
                double toX = container.getLayoutX() + getWidth();
                if (toX > 0) {
                    toX = 0;
                }
                containerMove(toX);
            });
            nextButton.setOnMouseClicked(event -> {
                double toX = container.getLayoutX() - getWidth();
                if (toX < getWidth() - tabBox.getWidth()) {
                    toX = getWidth() - tabBox.getWidth();
                }
                containerMove(toX);
            });
            // 初始化数据
            addTabs(cfTabPane.getTabs());
        }

        /**
         * 添加 Tabs 到 HBox
         *
         * @param tabs
         */
        private void addTabs(List<? extends CFTab> tabs) {
            tabs.forEach(tab -> {
                tab.setToggleGroup(toggleGroup);// 设置toggleGroup
                tab.setCfTabPane(cfTabPane);
            });
            tabBox.getChildren().addAll(tabs.stream().map(CFTab::getTabNode).toList());
        }

        // 获取选中的CFTab
        private CFTab getSelectCFTab() {
            ToggleButton selectedToggle = (ToggleButton) toggleGroup.getSelectedToggle();
            if (selectedToggle == null) {
                return null;
            }
            return (CFTab) selectedToggle.getParent().getUserData();
        }

        private Bounds getSelectedBounds() {
            CFTab selectTab = getSelectCFTab();
            if (selectTab == null) {
                return null;
            }
            CFTab.TabNode tabNode = selectTab.getTabNode();
            return tabNode.localToParent(tabNode.getLayoutBounds());
        }

        private Timeline activeTimeline;

        /**
         * activeBar执行动画到选中的位置
         */
        private void activeBarMove() {
            Bounds bounds = getSelectedBounds();
            if (bounds == null) {
                return;
            }
            if (bounds.getWidth() == 0) { // 初始化阶段,绑定位置
                CFTab.TabNode tabNode = getSelectCFTab().getTabNode();
                activeBar.widthProperty().bind(tabNode.widthProperty());
                activeBar.xProperty().bind(Bindings.createObjectBinding((Callable<Number>) () -> {
                    Bounds selectedBounds = getSelectedBounds();
                    return selectedBounds.getMinX();
                }, tabNode.layoutBoundsProperty()));
                return;
            } else { // 解除绑定
                activeBar.widthProperty().unbind();
                activeBar.xProperty().unbind();
            }
            double toX = bounds.getMinX(), toWidth = bounds.getWidth();
            if (activeTimeline == null) {
                activeTimeline = new Timeline();
            }
            if (activeTimeline.getStatus() == Animation.Status.RUNNING) {
                activeTimeline.stop();
            }
            KeyFrame startKeyFrame = new KeyFrame(Duration.millis(0),
                    new KeyValue(activeBar.widthProperty(), activeBar.getWidth(), Interpolator.EASE_BOTH),
                    new KeyValue(activeBar.xProperty(), activeBar.getX(), Interpolator.EASE_BOTH));
            KeyFrame endKeyFrame = new KeyFrame(Duration.millis(250),
                    new KeyValue(activeBar.widthProperty(), toWidth),
                    new KeyValue(activeBar.xProperty(), toX));
            activeTimeline.getKeyFrames().setAll(startKeyFrame, endKeyFrame);
            activeTimeline.play();
            activeTimeline.setOnFinished(event -> requestFocusTabButton());
        }

        private Timeline containerTimeline = new Timeline();

        /**
         * TabBox 移动
         */
        private void containerMove(Double toX) {
            Bounds bounds = getSelectedBounds();
            if (bounds == null) {
                return;
            }
            if (containerTimeline == null) {
                containerTimeline = new Timeline();
            }
            if (containerTimeline.getStatus() == Animation.Status.RUNNING) {
                containerTimeline.stop();
            }
            double formX = container.getLayoutX();
            if (toX == null) {
                if (bounds.getMaxX() > getWidth() - formX) { // 选中的tab超过右侧,向左移动.
                    toX = getWidth() - bounds.getMaxX();
                } else if (formX < 0 && bounds.getMinX() < -formX) { // 选中的tab超过左侧,向右移动.
                    toX = -bounds.getMinX();
                } else {
                    return;
                }
            }
            KeyFrame startKeyFrame = new KeyFrame(Duration.millis(0),
                    new KeyValue(container.layoutXProperty(), formX, Interpolator.EASE_BOTH));
            KeyFrame endKeyFrame = new KeyFrame(Duration.millis(250),
                    new KeyValue(container.layoutXProperty(), toX));
            containerTimeline.getKeyFrames().setAll(startKeyFrame, endKeyFrame);
            containerTimeline.play();
            containerTimeline.setOnFinished(event -> requestFocusTabButton());
        }

        // 选中的标签页获取焦点
        private void requestFocusTabButton() {
            Optional.ofNullable(getSelectCFTab()).ifPresent(cfTab -> cfTab.getTabButton().requestFocus());
        }
    }

}
